﻿using log4net.Core;
using Microsoft.Win32;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Net;
using System.Text;
using TransferPost.Beans;
using TransferPost.util;

namespace TransferPost.checkpoints
{
    /// <summary>
    /// add by zhzhh at 2019年12月14日 10点26分
    /// 根据前几天请朱健看下一下USN的技术，该检测点完成电脑上SVN提交情况的核验辅助工作
    /// 1、初始化每个磁盘的USN数据（未有索引）
    /// 2、检索所有的.SVN文件夹
    /// 3、遍历SVN文件夹，
    /// </summary>
    public class C_SVNCheck_Point : BaseCheckPoint
    {
        private NtfsUsnJournal _usnJournal = null;

        // 只针对SVN
        public static string SVN_DIC_NAME = ".svn";

        // svn pg svn:ignore   #获取属性值
        // svn ps svn:ignore 'value' path  #设置属性值
        public static string IGN_LST = @"obj bin LogFiles epoint.key packages AttachStorage BigFileUpLoadStorage "
            + @" UploaderTemp *.csproj.user *.suo target *.o *.lo *.la *.al *.user *.db *.obj *.pdb ~*.* 复件*.* 复件* *副本* "
            + @" *.suo Bin *.o *.lo *.la *.al .libs *.so *.so.[0-9]* *.a *.pyc *.pyo __pycache__ *.rej *~ #*# .#* .*.swp .DS_Store "
            + @" *.db *.settings *.classpath *.project .metadata .recommenders RemoteSystemsTempFiles config.ini logs .classpath .project .settings target"
            + @" *.log .externalToolBuilders WEB-INF *.lic rebel.xml .vs *.rar *.zip *.bak *.sln *.png *.jgp *.psd *.css *.gif *.bat *.sql "
            + @" 更新 *更新* *脚本* 脚本 *.exe *.xml *.doc *.docx *.xls *.xlsx *.ppt *.pptx *.txt *.dll *.cer *.resx *.pfx *UpgradeLog* *.mdf *.ldf *.opt"
            + @" svn *.svn *.json";

        public static List<string> NECESSARY_SUFFIXES = new List<string>() { ".cs", ".aspx", ".java", ".html" };

        public static string REG_IGN_PATH = @"Software\Tigris.org\Subversion\Config\miscellany";

        public static string REG_IGN_KEY = "global-ignores";

        /// <summary>
        /// 
        /// </summary>
        /// <returns></returns>
        public override bool PrepareCheck(object obj)
        {
            string Content = "";

            Content = string.Format("开始Usn检测选中盘符是否具备条件...");

            PopUpCheckProcessEvent(new MsgBean(Content), null);

            //DriveInfo[] volumes = DriveInfo.GetDrives();

            // 针对所有的磁盘进行USN文件创建，
            // 如果有U潘什么的，要拔掉的
            // 如果电脑上本来就用了EVERYTHING这种软件的话，是不用创建的，所以要判断状态
            Dictionary<string, DriveInfo> dicDevicesAndInfos = (Dictionary<string, DriveInfo>)obj;

            foreach (var item in dicDevicesAndInfos)
            {
                var v = item.Value;

                _usnJournal = new NtfsUsnJournal(v);

                Win32Api.USN_JOURNAL_DATA journalState = new Win32Api.USN_JOURNAL_DATA();
                NtfsUsnJournal.UsnJournalReturnCode status_save = _usnJournal.GetUsnJournalState(ref journalState);

                /*
                 AllocationDelta	8388608	
                FirstUsn	0	
                LowestValidUsn	0	
                MaxUsn	9223372036854710272	
                MaximumSize	33554432	
                NextUsn	193928	
                UsnJournalID	132207660945799754
                                 */

                // 说明已经创建过了
                if (status_save == NtfsUsnJournal.UsnJournalReturnCode.USN_JOURNAL_SUCCESS)
                {
                    Content = string.Format("磁盘[{0}]符合检测条件.", v.Name);

                    PopUpCheckProcessEvent(new MsgBean(Content), null);
                }
                else
                {
                    // 需要创建
                    NtfsUsnJournal.UsnJournalReturnCode rtn = _usnJournal.CreateUsnJournal(1000 * 1024, 16 * 1024);

                    Content = string.Format("Create->{0} elapsed time {1}(ms)",
                       "CreateUsnJournal()", NtfsUsnJournal.ElapsedTime.Milliseconds.ToString());
                    PopUpCheckProcessEvent(new MsgBean(Content), null);

                    if (rtn == NtfsUsnJournal.UsnJournalReturnCode.USN_JOURNAL_SUCCESS)
                    {
                        Content = (string.Format("磁盘[{0}]创建USN文件成功.", v.Name));
                        PopUpCheckProcessEvent(new MsgBean(Content), null);
                    }
                    else
                    {
                        Content = (string.Format("磁盘[{0}]创建USN文件失败.{1},无法检测...请联系ZHZHH5425", v.Name, rtn.ToString()));
                        PopUpCheckProcessEvent(new MsgBean(Level.Error, Content), null);

                        return false;
                    }
                }

                // 不要释放了，不然下次搜索还要用的。
                // 经过验证，它跟everything共用
                //NtfsUsnJournal.UsnJournalReturnCode status_delete = _usnJournal.DeleteUsnJournal(journalState);
            }

            return base.PrepareCheck(obj);
        }

        /// <summary>
        /// 
        /// </summary>
        /// <returns></returns>
        public override RtnBean DoCheck(object obj)
        {
            List<SVNCheckBean> rtns = new List<SVNCheckBean>();
            Dictionary<string, DriveInfo> dicDevicesAndInfos = (Dictionary<string, DriveInfo>)obj;

            foreach (var item in dicDevicesAndInfos)
            {
                var v = item.Value;

                string Content = string.Format("正在扫描[{0}]，一般要几分钟，开着先忙别的...", v.Name);
                PopUpCheckProcessEvent(new MsgBean(Level.Info, Content), null);

                Dictionary<ulong, FSNode> dicFRNLookup = new Dictionary<ulong, FSNode>();

                _usnJournal = new NtfsUsnJournal(v);

                NtfsUsnJournal.UsnJournalReturnCode rtnCode = _usnJournal.GetNtfsVolumeFolders(SVN_DIC_NAME, out dicFRNLookup);
                if (rtnCode == NtfsUsnJournal.UsnJournalReturnCode.USN_JOURNAL_SUCCESS)
                {
                    string szDriveLetter = v.Name.TrimEnd('\\');

                    // 这边过滤出.svn的文件夹然后再得出文件夹全路径
                    foreach (KeyValuePair<ulong, FSNode> oFSNode in dicFRNLookup.Where(x => x.Value.IsSvn))
                    {
                        string sFullPath = oFSNode.Value.FileName;
                        FSNode oParentFSNode = oFSNode.Value;

                        while (dicFRNLookup.TryGetValue(oParentFSNode.ParentFRN, out oParentFSNode))
                        {
                            sFullPath = string.Concat(oParentFSNode.FileName, "\\", sFullPath);
                        }
                        sFullPath = string.Concat(szDriveLetter, "\\", sFullPath);

                        //TODO:进行SVN检测等
                        SVNCheckBean bean = new SVNCheckBean(sFullPath.TrimEnd(SVN_DIC_NAME.ToCharArray()));
                        rtns.Add(bean);
                    }
                }

                //OutPutBootmMsg(string.Format("共检测到SVN目录共[{0}]处", lstScanRtn.Count));
                Content = string.Format("扫描[{0}]完成...SVN目录[{1}]处", v.Name, rtns.Count);
                PopUpCheckProcessEvent(new MsgBean(Level.Info, Content), null);
            }

            RtnBean rtn = new RtnBean();
            rtn.status = 1;
            rtn.par.Add("svn", rtns);

            return rtn;
        }

        /// <summary>
        /// TODO：
        /// 1-类的内聚性不好，暴露静态方法了，可以放到独立工具类以后，
        /// 2-长就先长吧
        /// ? 尚未添加
        /// A 新增未提交
        /// M 修改未提交
        /// ! 删除未提交
        /// </summary>
        /// <param name="bean"></param>
        /// <param name="output"></param>
        public static void AnalyOutPut(SVNCheckBean bean, string output)
        {
            if (output.Length < 1)
            {
                // 需要记录错误日志，特征未知

                return;
            }

            string str_low_output = output.ToLower();

            switch (output[0])
            {
                case 'M':
                    bean.modify_count++;

                    foreach (var item in NECESSARY_SUFFIXES)
                    {
                        if (str_low_output.EndsWith(item))
                        {
                            bean.necessary_modify_count++;
                            break;
                        }
                    }
                    break;

                case '?':
                    bean.nonversioned_count++;

                    foreach (var item in NECESSARY_SUFFIXES)
                    {
                        if (str_low_output.EndsWith(item))
                        {
                            bean.necessary_nonversioned_count++;
                            break;
                        }
                    }
                    break;

                case 'A':
                    bean.add_count++;

                    foreach (var item in NECESSARY_SUFFIXES)
                    {
                        if (str_low_output.EndsWith(item))
                        {
                            bean.necessary_add_count++;
                            break;
                        }
                    }
                    break;

                case '!':
                    bean.del_count++;

                    foreach (var item in NECESSARY_SUFFIXES)
                    {
                        if (str_low_output.EndsWith(item))
                        {
                            bean.necessary_del_count++;
                            break;
                        }
                    }
                    break;

                default:
                    //抛出异常
                    break;
            }

        }



        /// <summary>
        /// 
        /// </summary>
        /// <returns></returns>
        public static string GetRemotingSVNIgnores()
        {
            string result = "";

            try
            {
                WebRequest req = HttpWebRequest.Create(PostContext.URL_SVNIGNORECFG);
                req.Timeout = 5000;

                using (WebResponse wr = req.GetResponse())
                {
                    StreamReader sr = new StreamReader(wr.GetResponseStream(), Encoding.UTF8);
                    result = sr.ReadToEnd().Trim();
                }
            }
            catch (Exception)
            {
                throw;
            }

            return result;
        }



        /// <summary>
        /// 设置SVN的忽略项
        /// </summary>
        /// <returns></returns>
        public static void SetRegIgnores(string igs)
        {
            try
            {
                initRegPath();

                RegistryKey rsg = Registry.CurrentUser.OpenSubKey(C_SVNCheck_Point.REG_IGN_PATH, true);

                rsg.SetValue(C_SVNCheck_Point.REG_IGN_KEY, igs);

                rsg.Close();
            }
            catch (Exception)
            {
                throw;
            }
        }

        private static void initRegPath()
        {
            RegistryKey rsg = Registry.CurrentUser.OpenSubKey(C_SVNCheck_Point.REG_IGN_PATH);

            if (rsg == null)
            {
                Registry.CurrentUser.CreateSubKey(C_SVNCheck_Point.REG_IGN_PATH);
            }
        }

        public static string GetRegIgnores()
        {
            try
            {
                initRegPath();

                RegistryKey rsg = Registry.CurrentUser.OpenSubKey(C_SVNCheck_Point.REG_IGN_PATH, false);

                var igLst = rsg.GetValue(C_SVNCheck_Point.REG_IGN_KEY);

                rsg.Close();

                return igLst.ToString();
            }
            catch (Exception)
            {
                throw;
            }
        }
    }

}
